﻿using System;
using System.Collections.Generic;
using System.Globalization;
using Netbits.Common.Collections;
using Netbits.Common.Threading;
using Netbits.TelnetServer.Enums;
using Netbits.TelnetServer.Parameters;

namespace Netbits.TelnetServer
{
	public sealed class Command : ICloneable, IDisposable
	{
		#region Private Variables

		LockedKeyedCollection<string, ParameterBase> _Parameters;
		Lock _Lock;
		readonly string _Name;

		Action<NetbitsTelnetClientSession, Command> _Execute;
		Folder _Parent;
		bool _Invisible;
		string _Description;
		bool _Disposed;

		#endregion

		#region Constructor

		internal Command( Folder Parent_, string Name_ )
		{
			_Lock = new Lock();
			_Parameters = new LockedKeyedCollection<string, ParameterBase>( parameter => parameter.Name, _Lock );
			_Parent = Parent_;
			_Name = Name_;
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns the name of the command.
		/// </summary>
		public string Name
		{
			get
			{
				return _Name;
			}
		}

		/// <summary>
		/// Returns the description of the command or set this.
		/// </summary>
		public string Description
		{
			get
			{
				using( _Lock.Read() )
					return _Description;
			}
			set
			{
				using( _Lock.Write() )
					_Description = value;
			}
		}

		/// <summary>
		/// Returns the parent folder.
		/// </summary>
		public Folder Parent
		{
			get
			{
				return _Parent;
			}
			internal set
			{
				_Parent = value;
			}
		}

		/// <summary>
		/// Returns the execute delegate/action or set this.
		/// </summary>
		public Action<NetbitsTelnetClientSession, Command> Execute
		{
			get
			{
				using( _Lock.Read() )
					return _Execute;
			}
			set
			{
				using( _Lock.Write() )
					_Execute = value;
			}
		}

		/// <summary>
		/// Returns if this parameter is invisible in ?. or set this.
		/// </summary>
		public bool Invisible
		{
			get
			{
				return _Invisible;
			}
			set
			{
				_Invisible = value;
			}
		}

		/// <summary>
		/// Returns an enumerable parameter list.
		/// </summary>
		public IEnumerable<ParameterBase> Parameters
		{
			get
			{
				return _Parameters;
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Returns the specified typed parameter
		/// </summary>
		/// <typeparam name="TParam">typed of specified parameter</typeparam>
		/// <param name="Name_">name of the parameter</param>
		/// <returns>Typed specified parameter</returns>
		public TParam GetParameter<TParam>( string Name_ )
		{
			ParameterBase pb = GetParameter( Name_ );
			if( pb == null )
				return default( TParam );

			return Netbits.Common.General.Converter.ConvertTo<TParam>( pb );
		}

		/// <summary>
		/// Returns the specified parameters base-class
		/// </summary>
		/// <param name="Name_">Name of the parameter</param>
		/// <returns>Base-Class of specified parameter</returns>
		public ParameterBase GetParameter( string Name_ )
		{
			return _Parameters[Name_];
		}

		#endregion

		#region Internal Functions

		/// <summary>
		/// Add new parameter to this command.
		/// </summary>
		/// <param name="Parameter_">Parameter to add</param>
		internal void AddParameter( ParameterBase Parameter_ )
		{
			_Parameters.Add( Parameter_ );
			_Parameters.Sort();
		}

		/// <summary>
		/// Print information of this command and all parameters.
		/// </summary>
		/// <param name="Search_">Specified parameters.</param>
		internal void PrintInfo( NetbitsTelnetClientSession Client_, string Search_ )
		{
			string paramString = string.Empty;
			paramString += Client_.GetAttribute( EnumTelnetColors.Green, EnumTelnetColors.Nothing, EnumTelnetAttributes.Bright );
			paramString += "{0}";
			paramString += Client_.GetAttribute( EnumTelnetColors.Yellow, EnumTelnetColors.Nothing, EnumTelnetAttributes.Dim );
			paramString += " -- ";
			paramString += Client_.GetResetColorAttributes();
			paramString += "[{1}] {2}";

			string paramMustString = string.Empty;
			paramMustString += Client_.GetAttribute( EnumTelnetColors.Yellow, EnumTelnetColors.Nothing, EnumTelnetAttributes.Dim );
			paramMustString += "<";
			paramMustString += Client_.GetAttribute( EnumTelnetColors.Green, EnumTelnetColors.Nothing, EnumTelnetAttributes.Bright );
			paramMustString += "{0}";
			paramMustString += Client_.GetAttribute( EnumTelnetColors.Yellow, EnumTelnetColors.Nothing, EnumTelnetAttributes.Dim );
			paramMustString += "> -- ";
			paramMustString += Client_.GetResetColorAttributes();
			paramMustString += "[{1}] {2}";

			foreach( ParameterBase pb in _Parameters )
			{
				if( pb.Invisible || !pb.Name.StartsWith( Search_, true, CultureInfo.CurrentCulture ) )
					continue;

				if( pb.MustSet )
					Client_.Send( paramMustString, true, pb.Name, pb.Typename, pb.Description );
				else
					Client_.Send( paramString, true, pb.Name, pb.Typename, pb.Description );
			}
		}

		/// <summary>
		/// Find the specified parameter.
		/// </summary>
		/// <param name="Name_">Name of the parameter to find</param>
		/// <returns>returns the parameter or null</returns>
		internal IList<ParameterBase> FindParameter( string Name_ )
		{
			List<ParameterBase> parameters = new List<ParameterBase>();
			foreach( ParameterBase pb in _Parameters )
			{
				if( pb.Name.StartsWith( Name_, true, CultureInfo.CurrentCulture ) )
					parameters.Add( pb );
			}

			return parameters;	
		}

		#endregion

		#region ICloneable

		/// <summary>
		/// Clones this command and all parameters.
		/// </summary>
		/// <returns>The new command (clone)</returns>
		public object Clone()
		{
			Command c = new Command( _Parent, _Name );
			c.Description = _Description;
			c.Execute = _Execute;
			c.Invisible = _Invisible;

			foreach( ParameterBase pb in _Parameters )
			{
				Type t = pb.GetType();
				ParameterBase newpb = Activator.CreateInstance( t, pb.Name ) as ParameterBase;
				newpb.CopyFrom( pb );
				c.AddParameter( pb );
			}

			return c;
		}

		#endregion

		#region IDisposable

		/// <summary>
		/// Dispose command resources
		/// </summary>
		public void Dispose()
		{
			if( !_Disposed )
			{
				if( _Parameters != null )
				{
					foreach( ParameterBase pb in _Parameters )
						pb.Dispose();
					_Parameters.Clear();
					_Parameters = null;
				}

				if( _Lock != null )
				{
					_Lock.Dispose();
					_Lock = null;
				}

				_Execute = null;
				_Parent = null;

				_Disposed = true;
			}

			GC.SuppressFinalize( true );
		}

		#endregion
	}
}
